package com.logic.modular.game.core.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.comment.pojo.domain.logic.core.PvpMatch;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.logic.comment.util.RedisUtils;
import com.logic.modular.game.RankMove;
import com.logic.modular.game.battle.info.base.PvpPlayerInfo;
import com.logic.modular.game.battle.info.base.PvpPlayerInfoServiceImpl;
import com.logic.modular.game.battle.process.PlayerPvpServiceImpl;
import com.logic.modular.game.core.mapper.PvpMatchMapper;
import com.logic.modular.game.player.bag.service.BagItemServiceImpl;
import com.comment.pojo.domain.logic.domain.PvpRanks;
import com.comment.pojo.domain.logic.domain.PlayerStatus;
import com.logic.modular.game.player.info.service.PlayerStatusServiceImpl;
import com.logic.modular.game.PvpCity;
import com.logic.modular.game.player.match.service.PvpCityServiceImpl;
import com.logic.modular.game.player.match.service.PvpRanksServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.logic.modular.game.GameConstant.PVP_MATCH_RANK_ATTACK_MOVE1_KEY;
import static com.logic.modular.game.GameConstant.PVP_MATCH_RANK_MOVE_KEY;

/**
 * 对局任务服务
 */
@Slf4j
@Service
public class MatchTaskServiceImpl extends ServiceImpl<PvpMatchMapper, PvpMatch>
        implements SmartInitializingSingleton {
    @Resource
    private PvpCityServiceImpl pvpCityServiceImpl;
    @Resource
    private PvpPlayerInfoServiceImpl playerInfoService;
    @Resource
    private MatchEndServiceImpl pvpMatchEndServiceImpl;
    @Resource
    private BagItemServiceImpl bagItemServiceImpl;
    @Resource
    private RedisUtils redisUtils;
    @Resource
    private PlayerPvpServiceImpl playerPvpService;
    @Resource
    private PvpRanksServiceImpl pvpRanksServiceImpl;
    @Resource
    private PlayerStatusServiceImpl playerStatusServiceImpl;


    public static final String SURVIVAL_MATCH_IDS = "survivalMatchIds";
    public static final LoadingCache<String, Set<Long>> survivalMatchIds = Caffeine.newBuilder()
            .recordStats()
            .build(key -> Collections.emptySet());

    @Override
    public void afterSingletonsInstantiated() {
        matchHeart();
    }


    private void cacheMissingHeart() {
        LambdaQueryWrapper<PvpMatch> pvpMatchLambdaQueryWrapper = new LambdaQueryWrapper<>();
        pvpMatchLambdaQueryWrapper.eq(PvpMatch::getStatus, 1);

        Set<Long> activeHeartbeat =
                list(pvpMatchLambdaQueryWrapper).stream().map(
                        PvpMatch::getId
                ).collect(Collectors.toSet());

        Set<Long> missingHeartbeats;
        missingHeartbeats = activeHeartbeat;
        survivalMatchIds.put(SURVIVAL_MATCH_IDS, missingHeartbeats);
    }

    public void matchHeart() {
        ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
        scheduler.scheduleAtFixedRate(this::cacheMissingHeart, 10, 5, TimeUnit.SECONDS);
        scheduler.scheduleAtFixedRate(() -> { // 尝试结束游戏
            try {
                Set<Long> matchIds = survivalMatchIds.get(SURVIVAL_MATCH_IDS);
                if (matchIds != null) {
                    synchronized (matchIds) {
                        for (Long matchId : matchIds) {
                            pvpMatchEndServiceImpl.tryEnd(matchId);
                        }
                    }
                }
            } catch (Exception e) {
                log.error("缓存未命中", e);
            }
        }, 10, 1000, TimeUnit.MILLISECONDS);
        scheduler.scheduleAtFixedRate(this::productTask, 10, 60000, TimeUnit.MILLISECONDS);

        scheduler.scheduleAtFixedRate(this::rankMoveTask, 10, 1000, TimeUnit.MILLISECONDS);
        scheduler.scheduleAtFixedRate(this::attackCityTask, 10, 1000, TimeUnit.MILLISECONDS);
    }

    /**
     * 队伍移动 任务
     */
    private void rankMoveTask() {
        try {
            Set<Long> matchIds = survivalMatchIds.get(SURVIVAL_MATCH_IDS);
            if (matchIds == null || matchIds.isEmpty()) {
                return;
            }
            for (Long matchId : matchIds) {
                String key0 = String.format(PVP_MATCH_RANK_MOVE_KEY, matchId);
                String key1 = String.format(PVP_MATCH_RANK_ATTACK_MOVE1_KEY, matchId);
                redisUtils.hGetAll(key0).values().stream().map(item -> (RankMove) item).forEach(rankMove -> {
                    boolean before = rankMove.getEndTime().before(new Date());// 已经结束
                    if (before) {// 结束移动
                        Integer type = rankMove.getType();
                        if (type == 1) {
                            redisUtils.hDelete(key0, rankMove.getPlayerId() + "-" + rankMove.getRankNo());
                            PvpRanks select = pvpRanksServiceImpl.select(rankMove.getPlayerId(), rankMove.getRankNo());
                            select.setStatus(1);
                            pvpRanksServiceImpl.updateById(select);
                            redisUtils.hPut(key1, rankMove.getPlayerId() + "-" + rankMove.getRankNo(), rankMove);
                        }
                        if (type == 2) {
                            redisUtils.hDelete(key0, rankMove.getPlayerId() + "-" + rankMove.getRankNo());
                            PvpRanks select = pvpRanksServiceImpl.select(rankMove.getPlayerId(), rankMove.getRankNo());
                            select.setStatus(1);
                            pvpRanksServiceImpl.updateById(select);
                            redisUtils.hPut(key1, rankMove.getPlayerId() + "-" + rankMove.getRankNo(), rankMove);
                        }
                    }
                });
            }
        } catch (Exception e) {
            log.error("缓存未命中", e);
        }
    }

    /**
     * 攻打城池任务
     */
    private void attackCityTask() {
        try {
            Set<Long> matchIds = survivalMatchIds.get(SURVIVAL_MATCH_IDS);
            if (matchIds == null || matchIds.isEmpty()) {
                return;
            }
            for (Long matchId : matchIds) {
                String key1 = String.format(PVP_MATCH_RANK_ATTACK_MOVE1_KEY, matchId);
                redisUtils.hGetAll(key1).values().stream().map(item -> (RankMove) item).forEach(rankMove -> {
                    Long playerId = rankMove.getPlayerId();
                    PlayerStatus playerStatus = playerStatusServiceImpl.get(playerId);
                    if (playerStatus == null || playerStatus.getCurrentMatchId() == null || playerStatus.getCurrentMatchId() < 1) {
                        log.info("玩家:{},已退出对局,队伍:{},无法攻打城池", playerId, rankMove.getRankNo());
                        return;
                    }

                    boolean before = rankMove.getEndTime().before(new Date());// 已经结束
                    if (before) {
                        log.info("开始攻城");
                        try {
                            playerPvpService.attackCity(
                                    rankMove.getPlayerId(),
                                    rankMove.getRankNo(),
                                    rankMove.getTargetCityId()
                            );
                            redisUtils.hDelete(key1, rankMove.getPlayerId() + "-" + rankMove.getRankNo());
                        } catch (Exception e) {
                            log.error("攻城失败，开始回滚队伍状态和消耗", e);
                            // 人机回滚
                            if (playerId > 1000) {
                                bagItemServiceImpl.redemptionOfProp(rankMove.getCost());
                            }
                            // 通用回滚
                            PvpRanks attackerRanks = pvpRanksServiceImpl.select(playerId, rankMove.getRankNo());
                            attackerRanks.setStatus(1);
                            pvpRanksServiceImpl.updateById(attackerRanks);
                            PvpCity pvpCity = pvpCityServiceImpl.getPvpCity(rankMove.getTargetCityId(), matchId);
                            pvpRanksServiceImpl.obtainPvpRanKs(pvpCity.getOwnerUid()).stream()
                                    .filter(item -> Objects.equals(item.getCityId(), pvpCity.getCoreCityId()))
                                    .filter(item -> Objects.equals(item.getStatus(), 2))
                                    .forEach(item -> {
                                        item.setStatus(1);
                                        pvpRanksServiceImpl.updateById(item);
                                    });
                            // 删除
                            redisUtils.hDelete(key1, rankMove.getPlayerId() + "-" + rankMove.getRankNo());
                            throw e;
                        }
                    }
                });
            }
        } catch (Exception e) {
            log.error("缓存未命中", e);
        }
    }


    /**
     * 生产力任务
     */
    private void productTask() {
        try {
            Set<Long> result = survivalMatchIds.get(SURVIVAL_MATCH_IDS);
            if (result == null) {
                throw new RuntimeException("当前没有对局");
            }
            for (Long matchId : result) {
                // 获取玩家城池
                Map<String, PvpPlayerInfo> pvpPlayerInfoMap = playerInfoService.mapCache(matchId);
                pvpPlayerInfoMap.forEach((ownerUidStr, pvpPlayerInfo) -> {
                    Long ownerUid = Long.valueOf(ownerUidStr);
                    Set<Long> coreCityIds = pvpPlayerInfo.getCurrentCity();
                    coreCityIds.forEach(cityId -> {
                        PvpCity city = pvpCityServiceImpl.getCache(matchId, cityId);
                        if (city == null) {
                            return;
                        }
                        Long copperRate = city.getCopperRate();
                        if (copperRate > 0) {
                            bagItemServiceImpl.obtainBagItem(ownerUid, 4L, BigDecimal.valueOf(copperRate));
                        }
                        Long grainRate = city.getGrainRate();
                        if (grainRate > 0) {
                            bagItemServiceImpl.obtainBagItem(ownerUid, 5L, BigDecimal.valueOf(grainRate));
                        }
                        Long readyArmyRate = city.getReadyArmyRate();
                        if (readyArmyRate > 0) {
                            city.setReadyArmy(city.getReadyArmy() + readyArmyRate);
                            pvpCityServiceImpl.update(city);
                        }
                    });
                });
            }
        } catch (Exception e) {
            log.error("缓存未命中", e);
        }
    }
}